package com.relife.core.dao;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.relife.common.model.Page;
import com.relife.common.utils.StringUtil;
import com.relife.core.qbc.QbcQuery;
import com.relife.core.utils.DBUtil;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;



@Repository
@SuppressWarnings("unchecked")
public class BaseDao implements IBaseDao {
	@Autowired
	@Qualifier("sessionFactory")
	private SessionFactory sessionFactory;

	@Autowired
	@Qualifier("jdbcTemplate")
	private JdbcTemplate JdbcTemplate;

	private Session getSession() {
		return sessionFactory.getCurrentSession();
	}


	public <T> void save(T entity) {
		this.getSession().save(entity);
	}


	public <T> void update(T entity) {
		this.getSession().update(entity);
	}


	public int update(String sql, Object... objs) {
		return JdbcTemplate.update(sql, objs);
	}


	public void execute(String sql) {
		JdbcTemplate.execute(sql);
	}


	public <T> void saveOrUpdate(T entity) {
		this.getSession().saveOrUpdate(entity);
	}


	public <T> void merge(T entity) {
		this.getSession().merge(entity);

	}


	public <T> void delete(T entity) {
		this.getSession().delete(entity);
	}


	public <T> void updateOrSaveList(List<T> list) {
		int i = 1;
		for (T entity : list) {
			getSession().saveOrUpdate(entity);
			if (i % 200 == 0) {
				this.flush();
			}
			i++;
		}
	}


	public <T> void deleteList(List<T> list) {
		int i = 1;
		if (list != null) {
			for (T entity : list) {
				getSession().delete(entity);
				if (i % 200 == 0) {
					this.flush();
				}
				i++;
			}
		}
	}


	public void flush() {
		this.getSession().flush();
	}


	public void clear() {
		this.getSession().clear();
	}


	public <T> Criteria createCriteria(Class<T> cl) {
		return this.getSession().createCriteria(cl);
	}

	
	public <T> T load(Class<T> cl, Serializable id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(cl, id);
	}

	
	public <T> T get(Class<T> cl, Serializable id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().get(cl, id);
	}

	 
	public <T> T getByHql(String hql, Serializable id) {
		Assert.notNull(id, "id is required");
		Assert.notNull(hql, "id is required");
		return (T) getSession().createQuery(hql).setString("id", (String) id).uniqueResult();
	}

	 
	public <T> T get(Class<T> cl, String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		return (T) getSession().createCriteria(cl).add(Restrictions.eq(propertyName, value)).uniqueResult();
	}

	 
	public <T> T get(DetachedCriteria dc) {
		Criteria criteria = dc.getExecutableCriteria(this.getSession());
		return (T) criteria.uniqueResult();
	}

	 
	public <T> T get(QbcQuery qbc) {
		Criteria criteria = qbc.getDetachedCriteria().getExecutableCriteria(this.getSession());
		return (T) criteria.uniqueResult();
	}

	 
	public <T> T get(Class<T> cl, String sql, Object... objs) {
		return JdbcTemplate.queryForObject(sql, cl, objs);
	}

	 
	public Map<String, Object> get(String sql, Object... objs) {
		List<Map<String, Object>> list = JdbcTemplate.queryForList(sql, objs);
		if (list != null && list.size() == 1) {
			return list.get(0);
		} else if (list.size() > 1) {
			Assert.isTrue(list.size() == 1, "预定的结果集为1，实际返回的结果集为" + list.size());
			return new HashMap<String, Object>();

		} else {
			return new HashMap<String, Object>();
		}
	}

	 
	public <T> List<T> getList(Class<T> cl, Serializable[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		return getSession().createCriteria(cl).add(Restrictions.in("id", ids)).list();
	}

	 
	public <T> List<T> getList(Class<T> cl, String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		return getSession().createCriteria(cl).add(Restrictions.eq(propertyName, value)).list();
	}

	 
	public <T> List<T> getList(Class<T> cl, String propertyName, Object value, Order order) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		return getSession().createCriteria(cl).add(Restrictions.eq(propertyName, value)).addOrder(order).list();
	}

	 
	public <T> List<T> getList(T t, Order order) {
		Criteria c = getSession().createCriteria(t.getClass());
		if (order != null)
			c.addOrder(order);
		c.add(Example.create(t).enableLike(MatchMode.ANYWHERE));
		return c.list();
	}

	 
	public <T> List<T> getList(QbcQuery qbc) {
		Criteria criteria = qbc.getDetachedCriteria().getExecutableCriteria(this.getSession());
		criteria.setFirstResult(0);
		criteria.setMaxResults(0);
		return criteria.list();
	}

	 
	public <T> List<T> getListByHql(String hql, Object... objs) {
		Query query = this.getSession().createQuery(hql);
		if (objs != null) {
			for (int i = 0; i < objs.length; i++) {
				query.setParameter(i, objs[i]);
			}
		}
		return query.list();
	}

	 
	public <T> List<T> getList(Class<T> cl, Order order) {
		Criteria c = getSession().createCriteria(cl);
		if (order != null)
			c.addOrder(order);
		return c.list();
	}

	 
	public List<Map<String, Object>> getList(String sql, Object... objs) {
		return JdbcTemplate.queryForList(sql, objs);
	}

	 
	public <T> Page getPage(T entity, Page page, Order order) {
		Criteria criteria = getSession().createCriteria(entity.getClass());
		if (entity != null)
			criteria.add(Example.create(entity).enableLike(MatchMode.ANYWHERE));
		int total = Integer.parseInt(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
		page.setTotal(total);
		criteria.setProjection(null);
		if (order != null)
			criteria.addOrder(order);
		criteria.setFirstResult(page.getStart());
		criteria.setMaxResults(page.getRows());
		page.setList(criteria.list());
		return page;
	}

	 
	public Page getPage(Page page, DetachedCriteria dc) {
		Criteria criteria = dc.getExecutableCriteria(this.getSession());
		if (page.getPage() > 0 && page.getRows() > 0) {
			int total = Integer.parseInt(criteria.setProjection(Projections.rowCount()).uniqueResult().toString());
			page.setTotal(total);
			criteria.setFirstResult(page.getStart());
			criteria.setMaxResults(page.getRows());
			criteria.setProjection(null);
		}
		criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		page.setList(criteria.list());
		return page;
	}

	 
	public Page getPage(QbcQuery qbc, Page page) {
		Criteria crit = qbc.getDetachedCriteria().getExecutableCriteria(this.getSession());

		if (page.getPage() > 0 && page.getRows() > 0) {
			Integer total = Integer.parseInt(crit.setProjection(Projections.rowCount()).uniqueResult().toString());
			page.setTotal(total);
			crit.setProjection(null);
			crit.setFirstResult(page.getStart());
			crit.setMaxResults(page.getRows());
		}
		if (page.getOrder() != null)
			crit.addOrder(page.getOrderBy());

		crit.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		page.setList(crit.list());
		return page;
	}

	 
	public Page getPage(String sql, Page page, Object... objs) {
		page.setTotal(getCountJdbc("select count(*) from (" + sql + ")", objs));
		if (StringUtil.isNotEmpty(page.getSort())) {
			sql += " order by " + page.getSort() + " " + StringUtil.toString(page.getOrder());
		}
		sql = DBUtil.formatPageSql(sql, page);
		page.setList(JdbcTemplate.queryForList(sql, objs));
		return page;
	}

	 
	public Integer getCountJdbc(String sql, Object... obj) {
		return JdbcTemplate.queryForObject(sql, Integer.class, obj);
	}

	 
	public Integer getCount(QbcQuery qbc) {
		Criteria crit = qbc.getDetachedCriteria().getExecutableCriteria(this.getSession());
		return Integer.parseInt(crit.setProjection(Projections.rowCount()).uniqueResult().toString());
	}

	 
	public <T> Integer getMax(Class<T> cl, String propertyName) {
		Criteria crit = getSession().createCriteria(cl);
		return StringUtil.toInteger(crit.setProjection(Projections.max(propertyName)).uniqueResult());
	}

}
